/**
 * 在运行阶段（非编译阶段）根据类名（如Obj，可以从配置文件中读取）来创建对象（如obj）并调用其任一属性和方法（包括Private修饰的）。
 * 主要涉及两个类：java.lang.Class和java.lang.reflect（getDeclaredXXX：所有的，否则只是public的）
 * 0) 获取Class对象：clz=Class.forName()/Obj.class/obj.getClass()
 * 1) 创建对象：clz.newInstance()/clz.getConstructor.newInstance()
 * 2）使用属性：clz.getField().get(obj)/clz.getField().set(obj,value)
 * 3）调用方法：clz.getMethod().invoke(obj,args)
 */
package com.example.reflect;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class ReflectSample {
    public static void main(String[] args) {
        try {
            //通过反射创建对象
            Class<?> personClass = Class.forName("com.example.reflect.Person");
            /*两种等效替代
            Class personClass = Person.class;

            Person  person = new Person();
            Class personClass=person.getClass();*/

            Person person1 = (Person) personClass.newInstance();    //用无参构造器创建对象
            System.out.println("--------反射创建对象后正常属性赋值和方法调用-------");
            person1.setName("zhangsan");
            person1.setAge(20);
            person1.setEmail("xyz@163.com");
            person1.showInfo();

            //获取类的构造方法
            Constructor<?>[] constructors = personClass.getConstructors();//获取类的所有构造方法（多个）
            System.out.println("--------反射获取到的构造器--------");
            for (Constructor<?> constructor : constructors) {
                System.out.println(constructor.toString());
            }
            //通过反射获取的构造器来创建对象
            System.out.println("--------利用反射获取到的构造器构造对象--------");
            Constructor<?> constructor1 = personClass.getConstructor();  //获取类的无参构造方法（单个）
            Constructor<?> constructor2 = personClass.getConstructor(String.class, int.class, String.class);  //获取类的有参构造方法,需要指定参数类型（单个）
            Person person2 = (Person) constructor2.newInstance("lisi", 15, "abc@163.com");    //用有参构造器创建对象

            //通过反射获取属性
            System.out.println("--------反射获取到的属性--------");
            Field[] fields = personClass.getDeclaredFields();
            for (Field f : fields) {
                System.out.println(f.toString());
            }
            //使用反射获取属性的值
            Field field1 = personClass.getDeclaredField("name");
            Field field2 = personClass.getDeclaredField("age");
            Field field3 = personClass.getDeclaredField("email");
            field1.setAccessible(true);
            field3.setAccessible(true);
            field2.setAccessible(true);
            field2.set(person1, 30);
            System.out.println("--------反射获取到的属性值--------");
            System.out.println(field1.get(person1) + "---" + field2.get(person1) + "---" + field3.get(person1));

            //通过反射获取方法
            System.out.println("--------反射获取到的方法--------");
            Method[] methods = personClass.getDeclaredMethods();//获取类中的所有方法,包括私有的,默认,保护的,不包含继承的
            for (Method me1 : methods) {
                System.out.println(me1.toString());
            }
            //调用反射获取的方法
            System.out.println("--------反射方法调用--------");
            Method method1 = personClass.getMethod("showInfo");//获取单个方法（无参无返回值、无参有返回值）
            method1.invoke(person2);     //无参方法
            Method method2 = personClass.getDeclaredMethod("sum", int.class);
            method2.setAccessible(true);
            System.out.println(method2.invoke(person2, 10));  //有参方法
            Method method3 = personClass.getMethod("say",String.class); //获取静态方法
            method3.invoke(null,"Hello World!");    //参数null表示不由对象调用，也就是静态方法

            System.out.println("--------Teacher--------");
            //循环打印Teacher及其所有父类属性名
            System.out.println("--------子类和父类的所有属性--------");
            Class<?> clazz = Teacher.class;
            for(; clazz != Object.class ; clazz = clazz.getSuperclass()) {
                try {
                    for (Field f : clazz.getDeclaredFields()) {
                        System.out.println(f.getName());
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

            System.out.println("--------子类和父类的所有方法--------");
            for(Class<?> clz = Teacher.class; clz != Object.class; clz = clz.getSuperclass()) {
                try {
                    for (Method m:clz.getDeclaredMethods()) {
                        System.out.println(m.getName());
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
